﻿using Basic.FlowApplication.Config;
using Basic.FlowApplication.Interface;
using Basic.FlowApplication.Model;
using Basic.FlowApplication.Model.AppMsg;
using Basic.FlowApplication.Model.AuditLog;
using Basic.FlowExtendService;
using Basic.FlowModel.AuditCopyLog;
using Basic.FlowModel.AuditLogItem;
using Basic.FlowRemoteModel;
using Basic.FormRemoteModel.QueryInter.Model;
using Basic.HrRemoteModel;
using Basic.HrRemoteModel.Msg.Model;
using WeDonekRpc.Helper;
using WeDonekRpc.Helper.Log;

namespace Basic.FlowApplication.Extend
{
    internal class AuditMsgService : IAuditMsgService
    {
        private readonly IFlowAppService _App;
        private readonly IMsgService _Msg;
        private readonly IFlowConfig _Config;
        private readonly IFormInterService _Form;
        private readonly IFlowAuditCopyService _Copy;
        private readonly IFormValFormatService _ValFormat;
        public AuditMsgService ( IFlowAppService app,
            IMsgService msg,
            IFlowConfig config,
            IFormValFormatService valFormat,
            IFlowAuditCopyService copy,
            IFormInterService form )
        {
            this._ValFormat = valFormat;
            this._Copy = copy;
            this._Form = form;
            this._Config = config;
            this._App = app;
            this._Msg = msg;
        }
        private AuditFlowBody _Flow;
        private AuditLog _Log;
        private AfootAuditLogItem _CurItem;
        private FlowAppBody _AppBody;
        private readonly List<MsgAdd> _SendList = new List<MsgAdd>();
        private readonly List<ToVoidItem> _ToVoidList = new List<ToVoidItem>();
        private readonly List<MsgItem> _Dones = new List<MsgItem>();
        private readonly List<long> _CopyId = new List<long>();


        public void HandlerMsg ( AuditLog log, AfootAuditLogItem item, AuditFlowBody flow )
        {
            this._Log = log;
            this._CurItem = item;
            this._Flow = flow;
            this._Dones.Add(new MsgItem
            {
                LinkType = MsgConstDic.TodoLinkType,
                LinkPk = item.Id
            });
            if ( log.Status == FlowAuditLogStatus.已结束 && log.AuditStatus != FlowAuditStatus.已取消 && log.AuditStatus != FlowAuditStatus.已关闭 )
            {
                this._Dones.Add(new MsgItem
                {
                    LinkType = MsgConstDic.TodoLinkType,
                    LinkPk = log.BeginItemId
                });
            }
            this._LoadMyTodoMsg();
            this._LoadTodoMsg();
            if ( log.Status == FlowAuditLogStatus.进行中 )
            {
                this._LoadCopyMsg();
            }
            this._ToVoidMsg();
            this._Send();
        }
        private void _Send ()
        {
            if ( this._SendList.Count == 0 && this._ToVoidList.Count == 0 && this._Dones.Count == 0 )
            {
                return;
            }
            FlowMsgStatus status = FlowMsgStatus.已发送;
            if ( !this._Msg.Send(this._SendList, this._ToVoidList, this._Dones, out string error) )
            {
                status = FlowMsgStatus.发送失败;
                new ErrorLog(error, "发送待办信息失败!", "FlowMsg")
                {
                    {"Done",this._Dones },
                    {"ToVoid",this._ToVoidList },
                    {"Send",this._SendList }
                }.Save();
            }
            if ( this._CopyId.Count > 0 )
            {
                DateTime now = DateTime.Now;
                this._Copy.SetStatus(this._CopyId.ConvertAllToArray(a => new AuditCopyStatus
                {
                    Id = a,
                    Status = status,
                    SendTime = now,
                    Error = error
                }));
            }
        }
        /// <summary>
        /// 新增抄送消息
        /// </summary>
        private void _LoadCopyMsg ()
        {
            List<AuditCopyDto> msgs = this._Copy.CreateCopyMsg(this._Log, this._CurItem, this._Flow);
            if ( msgs.Count == 0 )
            {
                return;
            }
            MsgBody[] body = this._GetBody(this._Flow.Nodes[this._CurItem.NodeId]);
            MsgAddItem[] items = msgs.ConvertAllToArray(( a ) =>
            {
                this._CopyId.Add(a.Id);
                AuditItemInfo item = this._Log.Items.Find(c => c.Id == a.ItemId);
                FlowNode node = this._Flow.Nodes[a.NodeId];
                return new MsgAddItem
                {
                    EmpId = a.ToEmpId,
                    FormEmpId = a.FromEmpId,
                    LinkPk = a.Id,
                    MsgTitle = this._Log.DocTitle,
                    Priority = HrMsgPriority.普通,
                    Body = a.MsgType != FlowMsgType.审核开始 ? this._GetBody(node) : body,
                    Attrs = new Dictionary<string, object>
                    {
                       {"NodeName", node.NodeName },
                       {"AuditType", (int)node.AuditType },
                       {"AppName", this._AppBody.AppName }
                    }
                };
            });
            this._SendList.Add(new MsgAdd
            {
                AppId = this._Log.AppId,
                LinkType = MsgConstDic.CopyLinkType,
                MsgClassify = MsgConstDic.CopyMsgClassify,
                MsgType = HrMsgType.普通信息,
                Items = items
            });
        }
        /// <summary>
        /// 新增待办信息
        /// </summary>
        private void _LoadTodoMsg ()
        {
            AuditItemInfo[] items = this._Log.Items.FindAll(a => a.AuditState == FlowAuditItemStatus.审核进行中 && a.Status == FlowAuditItemStatus.审核进行中);
            if ( items.IsNull() )
            {
                return;
            }
            this._AppBody = this._App.Get(this._Log.AppId);
            string tag = null;
            if ( this._CurItem.AuditState == FlowAuditItemStatus.已回退 )
            {
                tag = this._Config.ReturnMsgTag;
            }
            else if ( this._CurItem.AuditState == FlowAuditItemStatus.打回修改 )
            {
                tag = this._Config.RepulseMsgTag;
            }
            MsgBody[] body = this._GetBody(this._Flow.Nodes[this._CurItem.NodeId]);
            MsgAddItem[] msgs = items.ConvertAll<AuditItemInfo, MsgAddItem>(c =>
            {
                FlowNode node = this._Flow.Nodes[c.NodeId];
                return new MsgAddItem
                {
                    EmpId = c.EmpId,
                    FormEmpId = this._CurItem.EmpId,
                    LinkPk = c.Id,
                    Tag = tag,
                    MsgTitle = this._Log.DocTitle,
                    Priority = HrMsgPriority.普通,
                    TimeOut = c.TimeOut,
                    Body = body,
                    Attrs = new Dictionary<string, object>
                    {
                        {"NodeName", node.NodeName },
                        {"AuditType", (int)node.AuditType },
                        {"AppName", this._AppBody.AppName }
                    }
                };
            });
            this._SendList.Add(new MsgAdd
            {
                AppId = this._Log.AppId,
                LinkType = MsgConstDic.TodoLinkType,
                MsgType = HrMsgType.待办消息,
                MsgClassify = MsgConstDic.TodoMsgClassify,
                Items = msgs,
            });
        }
        /// <summary>
        /// 作废消息
        /// </summary>
        private void _ToVoidMsg ()
        {
            if ( this._CurItem.NodeType == FlowNodeType.开始节点 )
            {
                this._ToVoidList.Add(new ToVoidItem
                {
                    LinkPk = [this._CurItem.Id],
                    LinkType = MsgConstDic.TodoLinkType,
                    Tag = this._Config.StaySubmitAuditTag
                });
            }
            long[] itemId = this._Log.Items.Convert(a => a.Status == FlowAuditItemStatus.已作废, a => a.Id);
            if ( !itemId.IsNull() )
            {
                this._ToVoidList.Add(new ToVoidItem
                {
                    LinkPk = itemId,
                    LinkType = MsgConstDic.TodoLinkType
                });
                long[] copyId = this._Copy.GetToVoidCopyId(itemId);
                if ( !copyId.IsNull() )
                {
                    this._ToVoidList.Add(new ToVoidItem
                    {
                        LinkPk = copyId,
                        LinkType = MsgConstDic.CopyLinkType
                    });
                }
            }
        }
        private readonly Dictionary<long, Dictionary<long, FormVal>> _FormCache = new Dictionary<long, Dictionary<long, FormVal>>();
        private Dictionary<long, FormVal> _GetFormBody ( long formId, FlowAppMsgTemplate[] form )
        {
            if ( this._FormCache.TryGetValue(formId, out Dictionary<long, FormVal> val) )
            {
                return val;
            }
            val = this._Form.GetFormValues(new FormValsGetArg
            {
                FormId = formId,
                LinkPK = this._Log.LinkPk,
                LinkType = this._Log.LinkType,
                Column = form.ConvertAll(a => new KeyValuePair<long, string>(a.ColId.Value, a.Keywords))
            });
            if ( val.Count > 0 )
            {
                val.ForEach(( c, k ) =>
                {
                    this._ValFormat.Format(k);
                });
            }
            this._FormCache.Add(formId, val);
            return val;
        }
        private MsgBody[] _GetBody ( FlowNode node )
        {
            if ( this._AppBody.Template.IsNull() )
            {
                return null;
            }
            long formId = node.FormId.GetValueOrDefault(this._Flow.DefFormId.Value);
            FlowAppMsgTemplate[] forms = this._AppBody.Template.FindAll(a => a.FormId == formId);
            if ( forms.IsNull() )
            {
                return null;
            }
            Dictionary<long, FormVal> vals = this._GetFormBody(formId, forms);
            return forms.ConvertAll(a =>
            {
                MsgBody body = new MsgBody
                {
                    Label = a.Label,
                    IsRed = a.IsRed,
                    IsRow = a.IsRow
                };
                if ( a.DataType == FlowAppMsgDataType.自定义 )
                {
                    body.Value = a.Value;
                }
                else if ( a.DataType == FlowAppMsgDataType.表单 )
                {
                    if ( vals.TryGetValue(a.ColId.Value, out FormVal val) )
                    {
                        body.Value = val.FormatString();
                    }
                }
                else if ( a.DataType == FlowAppMsgDataType.审批字段 )
                {
                    if ( a.Value == "AuditType" )
                    {
                        body.Value = node.AuditType.ToString();
                    }
                    else if ( a.Value == "NodeName" )
                    {
                        body.Value = node.NodeName;
                    }
                    else if ( a.Value == "AppName" )
                    {
                        body.Value = this._AppBody.AppName;
                    }
                    else if ( a.Value == "EndTime" )
                    {
                        body.Value = this._CurItem.EndTime.Value.ToString("yyyy-MM-dd HH:mm");
                    }
                }
                return body;
            });
        }

        /// <summary>
        /// 新增我的发起的待办
        /// </summary>
        private void _LoadMyTodoMsg ()
        {
            if ( this._CurItem.NodeType != FlowNodeType.开始节点 || this._CurItem.Status != FlowAuditItemStatus.审核通过 )
            {
                return;
            }
            this._AppBody = this._App.Get(this._Log.AppId);
            FlowNode node = this._Flow.Nodes[this._Flow.BeginNode];
            MsgAddItem add = new MsgAddItem
            {
                EmpId = this._CurItem.EmpId,
                FormEmpId = this._CurItem.EmpId,
                LinkPk = this._CurItem.Id,
                MsgTitle = this._Log.DocTitle,
                Priority = HrMsgPriority.普通,
                Body = this._GetBody(node),
                Attrs = new Dictionary<string, object>
                {
                    {"NodeName", node.NodeName },
                    {"AuditType", (int)node.AuditType },
                    {"AppName", this._AppBody.AppName }
                 }
            };
            this._SendList.Add(new MsgAdd
            {
                AppId = this._Log.AppId,
                LinkType = MsgConstDic.TodoLinkType,
                MsgType = HrMsgType.待办消息,
                MsgClassify = MsgConstDic.MyTodoMsgClassify,
                Items = new MsgAddItem[] { add },
            });
        }
    }
}
